iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0

介紹 App Router 其他比較奇巧型的路由(?)定義。

Parallel Router

除了定義 page 作為路由的主要頁面外,也可以定義額外的頁面區塊,跟主要頁面平行載入,並且這些額外區塊也有各自的 loading , error 等狀態。

console
├── @menu
│   └── page.tsx
├── layout.tsx
└── page.tsx

加上 @ 的目錄就是額外定義的頁面區塊。

可以在 layout 中去定義額外區塊的位置。

export interface LayoutProps {
  children: React.ReactNode;
  menu: React.ReactNode;
}

// ...

export default function Layout({ children, menu }: LayoutProps) {
  return (
    <div className="grid grid-cols-12">
      <div className="col-span-2">{menu}</div>
      <div className="col-span-10">{children}</div>
    </div>
  );
}

類似這樣,定義一個固定在左側的選單區塊。

除了做出獨立區塊外,也能用在因檢查而需要替換整個路由的情況,例如檢查是否登入,沒登入的話只能看到公開區塊的內容。

app
├── @public
│   └── default.tsx
├── layout.tsx
└── page.tsx
// layout.tsx

// ...

export default async function RootLayout({
  children,
  public: publicRoute,
}: {
  children: React.ReactNode;
  public: React.ReactNode;
}) {
  const isAuthenticated = false;

  return (
    <html lang="en">
      <body id="__next">
        <StyledEngineProvider injectFirst>
          <CssBaseline /> {/* 加上 CssBaseline */}
          <ThemeProvider theme={primaryTheme}>
						{/* 根據是否登入決定要顯示哪個路由 */}
            {isAuthenticated ? children : publicRoute}
          </ThemeProvider>
        </StyledEngineProvider>
      </body>
    </html>
  );
}

說是路由其實更類似 Vue 的 named slot 功能,只是藉由路由解析將一些原本需要全塞在 page 中的邏輯改成用目錄呈現,讓程式碼更獨立且乾淨。

Intercepting Routes

截擊路由,當某頁面想好好地導向某個路由時,從中攔截並替換成另外定義的路由。

可以跟 Parallel Router 並用,達成不切換頁面快顯目標路由內容的功能。

以點擊清單後彈出畫面顯示概要內容為例。

user
├── @modal
│   ├── (..)user
│   │   └── [id]
│   │       └── page.tsx
│   └── default.tsx
├── [id]
│   └── page.tsx
├── layout.tsx
└── page.tsx

先看到 @modal/(..)user/[id] 這段目錄結構,其中 (..) 就是 Intercepting Routes 主要的定義方式,有幾個種類:

(.) 參照從同一層路由開始
(..) 參照從上一層路由開始
(..)(..) 參照從上上層路由開始
(...) 參照從根路由開始,也就是從 app 那層開始

之後當 user/page 中嘗試要導向 user/[id] 的路由時,就會發現這邊有截斷路由的定義,並改用這邊的路由做顯示。

而因為 @modal 是 Parallel Route,就會變成沒切換頁面而是替換 modal 內容的效果,變成彈出視窗顯示。

// user/layout.tsx
export interface LayoutProps {
  children: React.ReactNode;
  modal: React.ReactNode;
}

// ...

export default function Layout({ children, modal }: LayoutProps) {
  return (
    <div>
      <h1>User Layout</h1>
      {children}
      {modal}
    </div>
  );
}
// user/@modal/(..)user/[id]/page.tsx
'user client';
import { Card, Modal } from '@mui/material';
import { useRouter } from 'next/navigation';

export interface IdProps {
  params: {
    id: string;
  };
}

export default function User({ params: { id } }: IdProps) {
  const router = useRouter();

  return (
    <Modal open>
      <Card>
        <h1>Welcome to User! {id}</h1>
        <button onClick={() => router.back()}>Back</button>
      </Card>
    </Modal>
  );
}

關閉 Modal 的方法不用 state ,而是用 router.back() 退回上個路由就可以。

這樣的結構有幾個好處:

  • 不用切換畫面就能看到目標的概要內容
  • 打開彈出視窗的路由可直接分享給別人,並直接看到詳細內容的頁面
  • 可以用瀏覽器的回上一頁功能回到清單,或是用下一頁功能打開剛剛看過的彈出畫面

上一篇
Next App Router 其二
下一篇
介紹 TanStackQuery
系列文
組裝前端開發工具箱,從 NX 入手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言